Skip to content

feat(tui): replace scrap screen with component storybook - #39548

Merged
kitlangton merged 1 commit into
v2from
tab-playground-sim
Jul 29, 2026
Merged

feat(tui): replace scrap screen with component storybook#39548
kitlangton merged 1 commit into
v2from
tab-playground-sim

Conversation

@kitlangton

@kitlangton kitlangton commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the scrap scratch screen with a component storybook — a built-in debug plugin hosting fixture-driven, full-screen simulations of real production components — and ships a round of tab-animation polish that came out of iterating in it.

Storybook

  • Index (Open storybook in the palette, Debug group): lists stories, ↑/↓/j/k + enter or number keys to open, esc home. Each story also gets its own palette entry (Storybook: Session tabs).
  • Session tabs story: a miniature app. The strip sits over a fake per-session transcript that switches with selection and whose tail line tracks the live run (● Working…, warning ⚠ Permission required…, red ✗ … failed, green ✓ Done). space starts a random tab (the selected tab included); s runs the selected tab so the edge flash and sweep can be watched under the cursor. Runs randomly complete (accent unread glow), fail (red glow), or pause needing input (breathing warning glow). New tabs open untitled and earn their title with the left-to-right wipe on first completion. t add, d close, r reset, arrows and 10 to move, drag to reorder, esc back to the index.
  • OPENCODE_STORY=1 boots into the index; OPENCODE_STORY=session-tabs boots straight into a story. This replaces OPENCODE_SCRAP, whose route id (scrap instead of opencode.scrap) had been broken since the flag was introduced.

Animation polish (production)

  • Brighter edge flash: the flash now blends toward its own color — a brighter stop on the same background-to-text ramp as the pulse (0.65 vs 0.45) — as a dedicated stage in blendTabPulseColor, instead of sharing the running stage's color.
  • Longer flash fades: edge flash 500ms → 800ms (rise held at ~80ms, all added time on the fade tail); completion pulse 900ms → 1200ms.
  • Glow title tinge: title characters sitting over an unread/attention glow tint toward its semantic color following the glow's own spatial falloff (unreadGlowIntensity), strongest by the number and neutral ~10 cells in. Non-glowing, non-fading titles still render as one plain string.
  • Number pulse: the tab number brightens faintly (15% at crest) as the running sweep passes beneath it. TabPulse reports the sweep's level at the number cell through a quantized onLevel callback (1/32 steps, 0 on idle), so updates only fire on visible change.
  • Startup route fix: initialRoute validation preserved plugin routes but silently dropped their data, breaking any deep link into a plugin page (including OPENCODE_STORY=<id> and OPENCODE_ROUTE-injected routes). Fixed in context/route.tsx.

How

  • feature-plugins/system/storybook/index.tsx: the Story type ({ id, title, render }), the story registry, the index screen, and the palette commands. Stories own their whole screen and bind esc back to the index. One plugin route (storybook) renders a story when data.story matches, the index otherwise.
  • feature-plugins/system/storybook/session-tabs.tsx: the tab lifecycle simulation plus the fake transcript. Fixture state lives in a keyed store (mirroring production identity semantics); a per-session outcomes record remembers how the last run ended, since unread state clears on select.
  • component/tab-pulse.tsx: flashColor option and blend stage; EDGE_FLASH_ATTACK; longer durations; onLevel emission from the render pass.
  • component/session-tabs.tsx: flashColor wiring, per-character characterColor (glow tinge + long-title fade share one code path), sweepLevel signal feeding numberColor.
  • context/route.tsx: plugin startup routes keep their data.
  • plugin/builtins.ts: registers opencode.storybook in place of opencode.scrap. Like all builtins it can be disabled with plugins: ["-opencode.storybook"].
  • app.tsx: the OPENCODE_STORY startup route replaces OPENCODE_SCRAP.

Scope

Follow-up to #39542 (previously stacked as #39546, auto-closed when the base branch merged). A pulse-variant A/B story was used to evaluate six alternative sweep animations and removed after the current sweep won; only the number pulse survived from that exploration. Adding stories for other components (and the controller seams they need) is future work.

Testing

  • bun typecheck in packages/tui
  • bun run test in packages/tui: 579 passed, 5 skipped (blend equivalence test extended to the four-stage pipeline; completion-pulse shape test updated for the new attack fraction)
  • oxlint and Prettier clean on changed files
  • Extensive interactive iteration in the storybook: flash timing/brightness, glow tinge falloff, number pulse subtlety, transcript behavior, and A/B comparison of nine sweep variants at production tab scale

Demo

See the recording on #39542: it drives an earlier revision of the session-tabs story end to end.

@kitlangton
kitlangton force-pushed the tab-playground-sim branch from a734323 to 7e545ff Compare July 29, 2026 18:17
@kitlangton kitlangton changed the title feat(tui): simulate tab lifecycle in playground feat(tui): replace scrap screen with component storybook Jul 29, 2026
@kitlangton
kitlangton force-pushed the tab-playground-sim branch 2 times, most recently from 71f7d26 to 5e844ef Compare July 29, 2026 18:33
@kitlangton
kitlangton force-pushed the tab-playground-sim branch from 5e844ef to d553e46 Compare July 29, 2026 19:50
@kitlangton
kitlangton merged commit 3c259fc into v2 Jul 29, 2026
7 of 9 checks passed
@kitlangton
kitlangton deleted the tab-playground-sim branch July 29, 2026 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant